﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;

namespace System.Windows.Controls
{
    /// <summary>
    /// Implements a portable base class for <see cref="System.Windows.Controls.Control"/> derived classes.
    /// </summary>
    public class ControlBase : System.Windows.Controls.Control
    {
        /// <summary>
        /// Initializes an instance of a <see cref="ControlBase"/> object.
        /// </summary>
        public ControlBase()
            : base()
        {
            this.IsEnabledChanged += new DependencyPropertyChangedEventHandler(DispatchOnIsEnabled);
        }

        void DispatchOnIsEnabled(object sender, DependencyPropertyChangedEventArgs e)
        {
            OnIsEnabledChanged(e);
        }

        private int     _ZIndexCached = 0;

        #region Focus

        /// <summary>
        /// Gets a boolean value indicating whether this <see cref="ControlBase"/> has focus
        /// </summary>
        public bool IsFocused
        {
            get { return FocusManager.GetFocusedElement() == this; }
        }

        #endregion

        #region Layout Helpers

        /// <summary>
        /// Gets/Sets the horizontal coordinate of the <see cref="ControlBase"/> instance.
        /// </summary>
        public double X
        {
            get { return (double)this.GetValue(Canvas.LeftProperty); }
            set
            {
                double x = (double)this.GetValue(Canvas.LeftProperty);
                if (x != value)
                {
                    this.SetValue(Canvas.LeftProperty, value);
                }
            }
        }

        /// <summary>
        /// Gets/Sets the vertical coordinate of the <see cref="ControlBase"/> instance.
        /// </summary>
        public double Y
        {
            get { return (double)this.GetValue(Canvas.TopProperty); }
            set
            {
                double y = (double)this.GetValue(Canvas.TopProperty);
                if (y != value)
                {
                    this.SetValue(Canvas.TopProperty, value);
                }
            }
        }

        /// <summary>
        /// Gets a <see cref="System.Windows.Rect"/> which defines the bounds of the <see cref="ControlBase"/> instance, relative to its parent container
        /// </summary>
        public Rect Bounds
        {
            get
            {
                return new Rect((double)this.GetValue(Canvas.LeftProperty), (double)this.GetValue(Canvas.TopProperty), this.Width, this.Height);
            }
        }

        /// <summary>
        /// Gets a <see cref="System.Windows.Rect"/> which defines the bounds of the <see cref="ControlBase"/> instance, relative to itself
        /// </summary>
        public Rect ClientRect
        {
            get { return new Rect(0, 0, (Double.IsNaN(this.Width) ? this.ActualWidth : this.Width), (Double.IsNaN(this.Height) ? this.ActualHeight : this.Height)); }
        }

        #endregion

        #region Dependency Property Helpers

        /// <summary>
        /// A static helper method for registering a <see cref="System.Windows.DependencyProperty"/>.
        /// </summary>
        /// <param name="controlType">The <see cref="System.Type"/> for the control to which the property belongs</param>
        /// <param name="name">The name of the property</param>
        /// <param name="propertyType">The <see cref="System.Type"/> for the value of the property</param>
        /// <returns>A <see cref="System.Windows.DependencyProperty"/> instance</returns>
        protected static DependencyProperty RegisterDependencyProperty(Type controlType, string name, Type propertyType)
        {
            DependencyProperty prop = DependencyProperty.Register(name, propertyType, controlType, new PropertyMetadata(new PropertyChangedCallback(OnHandleDependencyPropertyChanged)));
            return prop;
        }

        /// <summary>
        /// A static method for handling <see cref="System.Windows.DependencyProperty"/> changes.
        /// </summary>
        /// <param name="obj">The <see cref="System.Windows.DependencyObject"/> against which the property change should be applied</param>
        /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> event argument</param>
        protected static void OnHandleDependencyPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (obj is ControlBase)
            {
                ((ControlBase)obj).OnDependencyPropertyChanged(e);
            }
        }

        /// <summary>
        /// A virtual handler for processing <see cref="System.Windows.DependencyProperty"/> changes in derived controls.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> event argument</param>
        protected virtual void OnDependencyPropertyChanged(DependencyPropertyChangedEventArgs e)
        {

        }

        #endregion

        #region State Handlers

        /// <summary>
        /// A virtual handler for processing changes to <see cref="System.Windows.Controls.Control.IsEnabled"/> property changes.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> event argument</param>
        protected virtual void OnIsEnabledChanged(DependencyPropertyChangedEventArgs e)
        {

        }

        /// <summary>
        /// A helper method for changing visual state
        /// </summary>
        /// <param name="useTransitions">A boolean indicating whether to use transitions or not</param>
        /// <param name="stateName">The name of the state to assume</param>
        /// <returns>A boolean indicating whether the state could be changed</returns>
        protected bool GoToState(bool useTransitions, string stateName)
        {
            return VisualStateManager.GoToState(this, stateName, useTransitions);
        }

        #endregion

        #region Template Helpers

        /// <summary>
        /// Provides a templated method for finding a template child
        /// </summary>
        /// <typeparam name="T">The expected type of the child element</typeparam>
        /// <param name="name">The name of the child element to find</param>
        /// <returns>An instance of T or null if the item has not been found</returns>
        protected T VerifyTemplateChild<T>(string name) where T:class
        {
            T element = GetTemplateChild(name) as T;
            if (element == null && DesignerProperties.GetIsInDesignMode(this))
            {
                throw new InvalidOperationException(String.Format("Template for {0} missing required element, Name={1}, Type={2}", this.GetType().Name, name, typeof(T)));
            }

            return element;
        }

        #endregion

        #region Z-Order Helpers

        /// <summary>
        /// Gets/Sets the logical z-index of the <see cref="ControlBase"/> instance relative to parent the z-ordering stack.
        /// </summary>
        public int ZIndex
        {
            get { return (int)GetValue(Canvas.ZIndexProperty); }
            set 
            {
                try
                {
                    base.SetValue(Canvas.ZIndexProperty, value);
                }
                catch
                {

                }
            }
        }

        /// <summary>
        /// Brings the <see cref="ControlBase"/> instance to the front of the parent z-ordering stack.
        /// </summary>
        public virtual void BringToFront()
        {
            if (this.Parent != null)
            {
                _ZIndexCached = this.ZIndex;

                if (this.Parent is Panel)
                {
                    this.ZIndex = ((Panel)this.Parent).Children.Count - 1;
                }
            }
        }

        /// <summary>
        /// Sends the <see cref="ControlBase"/> instance to the back of the parent z-ordering stack.
        /// </summary>
        public virtual void SendToBack()
        {
            if (this.Parent != null)
            {
                _ZIndexCached = this.ZIndex;

                if (this.Parent is Panel)
                {
                    this.ZIndex = 0;
                }
            }
        }

        internal int ZIndexCached
        {
            get { return _ZIndexCached; }
            set { _ZIndexCached = value; }
        }

        /// <summary>
        /// Restores the <see cref="ControlBase"/> instance to its original z-index
        /// </summary>
        public virtual void RestoreZIndex()
        {
            this.ZIndex = _ZIndexCached;
        }

        #endregion
    }
}
